#include "MapUtils.h"
#include <e32math.h>

const TReal64 kTilesResolutions[] = { 
		(TReal64)360/(TReal64)(1<<(8+11)),
		(TReal64)360/(TReal64)(1<<(8+12)),
		(TReal64)360/(TReal64)(1<<(8+13)),
		(TReal64)360/(TReal64)(1<<(8+14)),
		(TReal64)360/(TReal64)(1<<(8+15)),
		(TReal64)360/(TReal64)(1<<(8+16)),
		(TReal64)360/(TReal64)(1<<(8+17)),
		(TReal64)360/(TReal64)(1<<(8+18))
#if defined(ONEMAP) // ONEMAP has one additional zoom level
		,(TReal64)360/(TReal64)(1<<(8+19))
#endif
};

const TReal64 kTilesResolutionsSVY21[] = { // m per pixel
		76.4372195411057, 38.2186097705529, 19.1093048852764, 9.55465244263822,
		4.77732622131911, 2.38866311065955, 1.19433155532978, 0.597165777664889,
		0.298450596901194};

// taken from Config.js and converted by http://dominoc925.blogspot.com/p/svy21-coordinate-converter.html
// this.tileBoundsSVY21 = [2297.51063567329, 54300.6320821656];
const TReal64 kSVY21BoundsLat = 92.4396788232422;
const TReal64 kSVY21BoundsLong = 50.7553193359375;

const TReal64 kTileOrigE = -5878011.897433;
const TReal64 kTileOrigN = 10172511.897433;

const TReal64 kAlphaE = 111286.842714206, kBetaE = -11527281.111126;
const TReal64 kAlphaN = 110643.182524163, kBetaN = -112459.217383763;



// =====================================
// http://dominoc925.blogspot.com/p/svy21-coordinate-converter.html
TReal ELLIPSOID_SEMIMAJORAXIS=6378137.0;
TReal ELLIPSOID_ECCENTRICITY=0.0818191908426215;
TReal ELLIPSOID_FLATTENING=0.00335281066474746;
TReal PROJ_NATURALORIGINLATITUDE=1.36666666666667;
TReal PROJ_NATURALORIGINLONGITUDE=103.833333333333;
TReal PROJ_SCALEFACTOR=1.0;
TReal PROJ_FALSEEASTINGS=28001.642;
TReal PROJ_FALSENORTHINGS=38744.572;

TENCoordinate::TENCoordinate(TReal aEasting, TReal aNorthing) : iEasting(aEasting), iNorthing(aNorthing) {}

TReal MathUtils::Tan(TReal aX) { TReal ret; Math::Tan(ret, aX); return ret; }
TReal MathUtils::Cos(TReal aX) { TReal ret; Math::Cos(ret, aX); return ret; }
TReal MathUtils::Sin(TReal aX) { TReal ret; Math::Sin(ret, aX); return ret; }
TReal MathUtils::Sqrt(TReal aX) { TReal ret; Math::Sqrt(ret, aX); return ret; }
TReal MathUtils::Ln(TReal aX) { TReal ret; Math::Ln(ret, aX); return ret; }
TReal MathUtils::ATan(TReal aX) { TReal ret; Math::ATan(ret, aX); return ret; }
TReal MathUtils::Exp(TReal aX) { TReal ret; Math::Exp(ret, aX); return ret; }
TInt MathUtils::Int(TReal aX) { TInt32 ret; Math::Int(ret, aX); return ret; }
TInt MathUtils::Floor(TReal aX) { TInt32 ret; Math::Int(ret, aX); if(aX < 0) ret--; return ret; }
TReal MathUtils::Pow(TReal aX, TReal aPower) { TReal ret; Math::Pow(ret, aX, aPower); return ret; }

const TReal MathUtils::PI = KPi;
TReal SecondEccentricity(){return(MathUtils::Sqrt(ELLIPSOID_ECCENTRICITY*ELLIPSOID_ECCENTRICITY/(1.0-ELLIPSOID_ECCENTRICITY*ELLIPSOID_ECCENTRICITY)));}
TReal CnvDegToRad(TReal deg){return(MathUtils::PI*deg)/180.0;}
TReal calc_A(TReal latRad,TReal lngRad){return(lngRad-CnvDegToRad(PROJ_NATURALORIGINLONGITUDE))*MathUtils::Cos(latRad);}
TReal calc_T(TReal latRad){return MathUtils::Tan(latRad)*MathUtils::Tan(latRad);}
TReal calc_C(TReal latRad){TReal value;value=MathUtils::Cos(latRad)*MathUtils::Cos(latRad)*MathUtils::Pow(ELLIPSOID_ECCENTRICITY,2.0);return value/(1.0-MathUtils::Pow(ELLIPSOID_ECCENTRICITY,2.0));}
TReal calc_v(TReal latRad){TReal value;value=1.0-MathUtils::Pow(ELLIPSOID_ECCENTRICITY*MathUtils::Sin(latRad),2.0);return ELLIPSOID_SEMIMAJORAXIS/MathUtils::Sqrt(value);}
TReal calc_M(TReal latRad){TReal value,e,term1,term2,term3,term4;e=ELLIPSOID_ECCENTRICITY;term1=1.0-(MathUtils::Pow(e,2)/4.0)-(3.0*MathUtils::Pow(e,4)/64.0)-(5.0*MathUtils::Pow(e,6)/256);term2=(3.0*MathUtils::Pow(e,2)/8.0)+(3.0*MathUtils::Pow(e,4)/32.0)+(45.0*MathUtils::Pow(e,6)/1024.0);term3=(15.0*MathUtils::Pow(e,4.0)/256.0)+(45.0*MathUtils::Pow(e,6)/1024.0);term4=(35.0*MathUtils::Pow(e,6)/3072);value=term1*latRad-term2*MathUtils::Sin(2.0*latRad)+term3*MathUtils::Sin(4.0*latRad)-term4*MathUtils::Sin(6*latRad);return value*ELLIPSOID_SEMIMAJORAXIS;}
TENCoordinate CnvLL2EN(TReal latDeg, TReal lngDeg)
{
	TReal ret,latRad,lngRad,A,T,C,M,v,M0,easting,northing,e2;
	e2=SecondEccentricity();
	latRad=CnvDegToRad(latDeg);
	lngRad=CnvDegToRad(lngDeg);
	A=calc_A(latRad,lngRad);T=calc_T(latRad);C=calc_C(latRad);M=calc_M(latRad);v=calc_v(latRad);M0=calc_M(CnvDegToRad(PROJ_NATURALORIGINLATITUDE));
	easting=A+((1.0-T+C)*MathUtils::Pow(A,3)/6.0)+(5.0-18.0*T+T*T+72.0*C-58.0*MathUtils::Pow(e2,2))*MathUtils::Pow(A,5)/120.0;
	easting=PROJ_FALSEEASTINGS+PROJ_SCALEFACTOR*v*easting;
	northing=(A*A/2.0)+((5.0-T+9.0*C+4.0*C*C)*MathUtils::Pow(A,4)/24.0)+((61.0-58.0*T+T*T+600.0*C-330.0*MathUtils::Pow(e2,2))*MathUtils::Pow(A,6)/720.0);
	northing=(PROJ_FALSENORTHINGS+PROJ_SCALEFACTOR*(M-M0+v*MathUtils::Tan(latRad)*northing));
	return TENCoordinate(easting, northing);
	}
// =====================================

TInt Long2TileX(TReal64 lon, TInt z) 
{ 
	return (TInt)(MathUtils::Int((lon + 180.0) / 360.0 * MathUtils::Pow(2.0, z))); 
}
 
TInt Lat2TileY(TReal64 lat, TInt z)
{ 
	return (TInt)(MathUtils::Floor((1.0 - MathUtils::Ln( MathUtils::Tan(lat * MathUtils::PI/180.0) + 1.0 / MathUtils::Cos(lat * MathUtils::PI/180.0)) / MathUtils::PI) / 2.0 * MathUtils::Pow(2.0, z))); 
}

TReal64 TileY2Lat(TInt y, TInt z) 
{
	TReal64 n = MathUtils::PI - 2.0 * MathUtils::PI * y / MathUtils::Pow(2.0, z);
	return 180.0 / MathUtils::PI * MathUtils::ATan(0.5 * (MathUtils::Exp(n) - MathUtils::Exp(-n)));
}

TReal YScale(const TCoordinate& aCoord)
	{
#if defined(MAPQUEST)
	return MathUtils::Cos(aCoord.Latitude() * MathUtils::PI / 180);
#elif defined(ONEMAP)
	return 1;
#endif
	}


TInt ZoomLevels()
	{
	return sizeof(kTilesResolutions)/sizeof(kTilesResolutions[0]);
	}

